package repo

import (
	"context"
	"fmt"
	"github.com/go-kratos/kratos/v2/log"
	"gotham-plan-manage/internal/domain/model/plan-aggregate/aggregate"
	"gotham-plan-manage/internal/domain/model/plan-aggregate/entity"
	"gotham-plan-manage/internal/domain/repo"
	"gotham-plan-manage/internal/infra/database/mysql/plan/convertor"
	"gotham-plan-manage/internal/infra/database/mysql/plan/dao"
	"gotham-plan-manage/internal/infra/database/mysql/plan/po"
	"gotham-plan-manage/pkg/jwt_util"
	"sync"
)

/*
   @author:zhongyang
   @date:2023/4/2
   @description:计划管理仓储层实现
   @tips：
	1.其实只有app层需要做一些领域操作的时候repo层才需要返回聚合根，所以很多地方的代码可能还需要修改。
*/

type PlanManageRepo struct {
	planManageConvertor convertor.PlanManageConvertor
	data                *Data
	log                 *log.Helper
}

func (p *PlanManageRepo) GetFinishTaskItemTotalByDateRange(ctx context.Context, planID string, startDate string, endDate string) (int32, error) {
	var wg sync.WaitGroup
	var mu sync.Mutex
	// 获取时间段内的待办
	taskDao := dao.NewTaskDao()
	var res int32
	taskPOs, err := taskDao.FindAllByPlanIdAndDateRange(p.data.DB(ctx), planID, startDate, endDate)
	if err != nil {
		return 0, err
	}
	itemDao := dao.NewTaskItemDao()
	// 然后分批去找待办数，最后返回
	for _, taskPO := range taskPOs {
		wg.Add(1)
		taskPO := taskPO
		go func() {
			defer func() {
				wg.Done()
			}()
			total, err := itemDao.GetTotalByTaskId(p.data.DB(ctx), taskPO.TodoTaskId)
			if err != nil {
				p.log.Errorf("PlanManageRepo.GetFinishTaskItemTotalByDateRange err:%v", err)
			}
			mu.Lock()
			res += int32(total)
			mu.Unlock()
		}()
	}
	wg.Wait()
	return res, nil
}

func (p *PlanManageRepo) GetTaskItemTotalByTaskId(ctx context.Context, taskId string) (int64, error) {
	itemDao := dao.NewTaskItemDao()
	count, err := itemDao.GetTotalByTaskId(p.data.DB(ctx), taskId)
	if err != nil {
		return 0, err
	}
	return count, nil
}

func (p *PlanManageRepo) FindAllTaskItemByPlanId(ctx context.Context, planID string) ([]*entity.TaskItem, error) {
	itemDao := dao.NewTaskItemDao()
	taskPOs, err := itemDao.FindAllByPlanId(p.data.DB(ctx), planID)
	if err != nil {
		return nil, err
	}
	return p.planManageConvertor.ConvertPO2TaskItemEntities(taskPOs), nil
}

func NewPlanManageRepo(data *Data, logger log.Logger) repo.PlanManageIRepo {
	return &PlanManageRepo{data: data, log: log.NewHelper(logger)}
}

func (p *PlanManageRepo) CreatePlan(ctx context.Context, plan *aggregate.PlanAggregate) error {
	// 创建计划
	PlanPO := p.planManageConvertor.CreatePlanPO(plan)
	planDao := dao.NewPlanDao()
	err := planDao.Create(p.data.DB(ctx), PlanPO)
	if err != nil {
		return err
	}
	// 创建计划详情
	planDetail := p.planManageConvertor.CreatePlanDetailPO(plan)
	detailDao := dao.NewPlanDetailDao()
	err = detailDao.Create(p.data.DB(ctx), planDetail)
	if err != nil {
		return err
	}
	return nil
}

func (p *PlanManageRepo) DeleteTaskByPlanId(ctx context.Context, plan *aggregate.PlanAggregate) error {
	taskPO := p.planManageConvertor.DeleteTaskPO(plan)
	taskDao := dao.NewTaskDao()
	return taskDao.DeleteByPlanId(p.data.DB(ctx), taskPO)
}

func (p *PlanManageRepo) DeleteTaskItemByPlanId(ctx context.Context, plan *aggregate.PlanAggregate) error {
	taskItemPO := p.planManageConvertor.DeleteTaskItemPO(plan)
	taskItemDao := dao.NewTaskItemDao()
	return taskItemDao.DeleteByPlanId(p.data.DB(ctx), taskItemPO)
}

func (p *PlanManageRepo) DeletePlan(ctx context.Context, plan *aggregate.PlanAggregate) error {
	planPO := p.planManageConvertor.DeletePlanPO(plan)
	planDetailPO := p.planManageConvertor.DeletePlanDetailPO(plan)
	planReportPO := p.planManageConvertor.DeletePlanReportPO(plan)
	questionPO := p.planManageConvertor.DeleteQuestionPO(plan)
	planDao := dao.NewPlanDao()
	planDetailDao := dao.NewPlanDetailDao()
	reportDao := dao.NewPlanReportDao()
	questionDao := dao.NewQuestionDao()
	err := planDao.Delete(p.data.DB(ctx), planPO)
	if err != nil {
		return err
	}
	err = planDetailDao.Delete(p.data.DB(ctx), planDetailPO)
	if err != nil {
		return err
	}
	err = reportDao.DeleteByPlanId(p.data.DB(ctx), planReportPO)
	if err != nil {
		return err
	}
	err = questionDao.DeleteByPlanId(p.data.DB(ctx), questionPO)
	if err != nil {
		return err
	}
	return nil
}

// FindPlanById 只获取计划实体的内容
func (p *PlanManageRepo) FindPlanById(ctx context.Context, planId string) (*aggregate.PlanAggregate, error) {
	planDao := dao.NewPlanDao()
	planPO, err := planDao.GetOne(p.data.DB(ctx), planId)
	if err != nil {
		return nil, err
	}
	planAggregate := p.planManageConvertor.CreatePlanAggregateByOption(convertor.WithPlanPO(planPO))
	return planAggregate, nil
}

func (p *PlanManageRepo) FindCompletePlanById(ctx context.Context, planId string) (*aggregate.PlanAggregate, error) {
	planDao := dao.NewPlanDao()
	planPO, err := planDao.GetOne(p.data.DB(ctx), planId)
	if err != nil {
		return nil, err
	}
	detailDao := dao.NewPlanDetailDao()
	detailPO, err := detailDao.GetOne(p.data.DB(ctx), planId)
	if err != nil {
		return nil, err
	}
	taskDao := dao.NewTaskDao()
	tasks, err := taskDao.FindByPlanId(p.data.DB(ctx), planId)
	if err != nil {
		return nil, err
	}
	var wg sync.WaitGroup
	taskItemDao := dao.NewTaskItemDao()
	for _, item := range tasks {
		wg.Add(1)
		task := item
		go func() {
			defer wg.Done()
			taskItems, err := taskItemDao.FindByTaskId(p.data.DB(ctx), task.TodoTaskId)
			if err != nil {
				fmt.Errorf("err:%v", err)
			}
			task.TaskItemPOs = taskItems
		}()
	}
	wg.Wait()
	planAggregate := p.planManageConvertor.CreatePlanAggregateByOption(convertor.WithPlanPO(planPO), convertor.WithPlanDetailPO(detailPO), convertor.WithTaskPOs(tasks))
	return planAggregate, nil
}

// FindPlanContainsDetailById 包含计划详情的计划实体
func (p *PlanManageRepo) FindPlanContainsDetailById(ctx context.Context, planId string) (*aggregate.PlanAggregate, error) {
	planDao := dao.NewPlanDao()
	planPO, err := planDao.GetOne(p.data.DB(ctx), planId)
	if err != nil {
		return nil, err
	}
	detailDao := dao.NewPlanDetailDao()
	detailPO, err := detailDao.GetOne(p.data.DB(ctx), planId)
	if err != nil {
		return nil, err
	}
	planAggregate := p.planManageConvertor.CreatePlanAggregateByOption(convertor.WithPlanPO(planPO), convertor.WithPlanDetailPO(detailPO))
	return planAggregate, nil
}

func (p *PlanManageRepo) EditPlan(ctx context.Context, planAggregate *aggregate.PlanAggregate) error {
	planPO := p.planManageConvertor.EditPlanPO(planAggregate)
	planDao := dao.NewPlanDao()
	return planDao.Save(p.data.DB(ctx), planPO)
}

func (p *PlanManageRepo) EditTarget(ctx context.Context, planAggregate *aggregate.PlanAggregate) error {
	planDetailPO := p.planManageConvertor.CreatePlanDetailPO(planAggregate)
	detailDao := dao.NewPlanDetailDao()
	return detailDao.Save(p.data.DB(ctx), planDetailPO)
}

func (p *PlanManageRepo) SubmitPlanReport(ctx context.Context, plan *aggregate.PlanAggregate) error {
	reportPO := p.planManageConvertor.CreatePlanReportPO(plan)
	reportDao := dao.NewPlanReportDao()
	return reportDao.Save(p.data.DB(ctx), reportPO)
}

func (p *PlanManageRepo) FindPlanContainsReportById(ctx context.Context, planId string) (*aggregate.PlanAggregate, error) {
	planDao := dao.NewPlanDao()
	planPO, err := planDao.GetOne(p.data.DB(ctx), planId)
	if err != nil {
		return nil, err
	}
	reportDao := dao.NewPlanReportDao()
	reportPO, err := reportDao.GetOne(p.data.DB(ctx), planId)
	if err != nil {
		return nil, err
	}
	return p.planManageConvertor.CreatePlanAggregateByOption(convertor.WithPlanPO(planPO), convertor.WithPlanReportPO(reportPO)), nil
}

func (p *PlanManageRepo) FindParentPlanListPage(ctx context.Context, page int, pageSize int, userId string) ([]*aggregate.PlanAggregate, int64, error) {
	planDao := dao.NewPlanDao()
	parentPlans, total, err := planDao.FindParentPlanListPage(p.data.DB(ctx), page, pageSize, userId)
	if err != nil {
		return nil, 0, err
	}
	var res []*aggregate.PlanAggregate
	for _, parentPlan := range parentPlans {
		res = append(res, p.planManageConvertor.CreatePlanAggregateByOption(convertor.WithPlanPO(parentPlan)))
	}
	return res, total, nil
}

func (p *PlanManageRepo) GetTaskTotalByPlanId(ctx context.Context, planId string, finished bool) (int64, error) {
	itemDao := dao.NewTaskItemDao()
	return itemDao.GetTotalByPlanId(p.data.DB(ctx), planId, finished)
}

func (p *PlanManageRepo) FindChildrenPlanListByParentId(ctx context.Context, parentId string, userID string) ([]*aggregate.PlanAggregate, error) {
	planDao := dao.NewPlanDao()
	childrenPlan, err := planDao.FindChildrenPlanListByParentId(p.data.DB(ctx), parentId, userID)
	if err != nil {
		return nil, err
	}
	var res []*aggregate.PlanAggregate
	for _, childPlan := range childrenPlan {
		res = append(res, p.planManageConvertor.CreatePlanAggregateByOption(convertor.WithPlanPO(childPlan)))
	}
	return res, nil
}

func (p *PlanManageRepo) FindPlanContainsTaskByPlanIdAndDate(ctx context.Context, planId string, executeDate string) (*aggregate.PlanAggregate, error) {
	planDao := dao.NewPlanDao()
	planPO, err := planDao.GetOne(p.data.DB(ctx), planId)
	if err != nil {
		return nil, err
	}
	taskDao := dao.NewTaskDao()
	taskPO, err := taskDao.GetOneByPlanIdAndDate(p.data.DB(ctx), planId, executeDate)
	if err != nil {
		return nil, err
	}
	return p.planManageConvertor.CreatePlanAggregateByOption(convertor.WithPlanPO(planPO), convertor.WithTaskPO(taskPO)), nil
}

func (p *PlanManageRepo) CreateTask(ctx context.Context, plan *aggregate.PlanAggregate) error {
	// 先判断是否需要新建待办
	if plan.Tasks[0].IsNew {
		taskPO, err := p.planManageConvertor.CreateTaskPO(plan)
		if err != nil {
			return err
		}
		taskDao := dao.NewTaskDao()
		err = taskDao.Create(p.data.DB(ctx), taskPO)
		if err != nil {
			return err
		}
	}
	// 创建待办项
	taskItemPO, err := p.planManageConvertor.CreateTaskItemPO(plan)
	if err != nil {
		return err
	}
	taskItemDao := dao.NewTaskItemDao()
	return taskItemDao.Create(p.data.DB(ctx), taskItemPO)
}

func (p *PlanManageRepo) DeleteTaskItem(ctx context.Context, taskItemId string) error {
	itemDao := dao.NewTaskItemDao()
	return itemDao.Delete(p.data.DB(ctx), taskItemId)
}

func (p *PlanManageRepo) DeleteTask(ctx context.Context, taskId string) error {
	// 先删除待办项
	itemDao := dao.NewTaskItemDao()
	err := itemDao.DeleteByTaskId(p.data.DB(ctx), taskId)
	if err != nil {
		return err
	}
	taskDao := dao.NewTaskDao()
	err = taskDao.Delete(p.data.DB(ctx), taskId)
	if err != nil {
		return err
	}
	return nil
}

func (p *PlanManageRepo) GetTaskItemById(ctx context.Context, taskItemId string) (*aggregate.PlanAggregate, error) {
	itemDao := dao.NewTaskItemDao()
	taskItemPO, err := itemDao.GetOne(p.data.DB(ctx), taskItemId)
	if err != nil {
		return nil, err
	}
	planDao := dao.NewPlanDao()
	planPO, err := planDao.GetOne(p.data.DB(ctx), taskItemPO.PlanId)
	taskDao := dao.NewTaskDao()
	taskPO, err := taskDao.GetOne(p.data.DB(ctx), taskItemPO.TodoTaskId)
	if err != nil {
		return nil, err
	}
	return p.planManageConvertor.CreatePlanAggregateByOption(convertor.WithPlanPO(planPO), convertor.WithTaskPO(taskPO), convertor.WithTaskItemPOs([]*po.TaskItemPO{taskItemPO})), nil
}

func (p *PlanManageRepo) GetTaskList(ctx context.Context, planId string, page int, pageSize int) (*aggregate.PlanAggregate, int64, error) {
	planDao := dao.NewPlanDao()
	planPO, err := planDao.GetOne(p.data.DB(ctx), planId)
	if err != nil {
		return nil, 0, err
	}
	taskDao := dao.NewTaskDao()
	tasks, total, err := taskDao.FindPageByPlanId(p.data.DB(ctx), jwt_util.GetUserId(ctx), planId, page, pageSize)
	if err != nil {
		return nil, 0, err
	}
	var wg sync.WaitGroup
	taskItemDao := dao.NewTaskItemDao()
	for _, item := range tasks {
		wg.Add(1)
		task := item
		go func() {
			defer wg.Done()
			taskItems, err := taskItemDao.GetTaskItemListByTaskId(p.data.DB(ctx), task.TodoTaskId, task.PlanId)
			if err != nil {
				p.log.Errorf("err:%v", err)
			}
			task.TaskItemPOs = taskItems
		}()
	}
	wg.Wait()
	planAggregate := p.planManageConvertor.CreatePlanAggregateByOption(convertor.WithPlanPO(planPO), convertor.WithTaskPOs(tasks))
	return planAggregate, total, nil
}

func (p *PlanManageRepo) GetTaskById(ctx context.Context, taskId string) (*aggregate.PlanAggregate, error) {
	taskDao := dao.NewTaskDao()
	taskPO, err := taskDao.GetOne(p.data.DB(ctx), taskId)
	if err != nil {
		return nil, err
	}
	planDao := dao.NewPlanDao()
	planPO, err := planDao.GetOne(p.data.DB(ctx), taskPO.PlanId)
	if err != nil {
		return nil, err
	}
	return p.planManageConvertor.CreatePlanAggregateByOption(convertor.WithPlanPO(planPO), convertor.WithTaskPOs([]*po.TaskPO{taskPO})), nil
}

func (p *PlanManageRepo) FinishTaskItem(ctx context.Context, plan *aggregate.PlanAggregate) error {
	taskItemPO := p.planManageConvertor.FinishTaskItemPO(plan)
	itemDao := dao.NewTaskItemDao()
	return itemDao.Finish(p.data.DB(ctx), taskItemPO)
}

func (p *PlanManageRepo) FindOneDayQuestions(ctx context.Context, planId string, taskDate string) (*aggregate.PlanAggregate, error) {
	questionDao := dao.NewQuestionDao()
	questionPOs, err := questionDao.FindOneDayQuestions(p.data.DB(ctx), planId, taskDate, jwt_util.GetUserId(ctx))
	if err != nil {
		return nil, err
	}
	return p.planManageConvertor.CreatePlanAggregateByOption(convertor.WithQuestionPOs(questionPOs)), nil
}

func (p *PlanManageRepo) SearchQuestions(ctx context.Context, planId string, content string) (*aggregate.PlanAggregate, error) {
	questionDao := dao.NewQuestionDao()
	questionPOs, err := questionDao.SearchQuestions(p.data.DB(ctx), jwt_util.GetUserId(ctx), planId, content)
	if err != nil {
		return nil, err
	}
	return p.planManageConvertor.CreatePlanAggregateByOption(convertor.WithQuestionPOs(questionPOs)), nil
}

func (p *PlanManageRepo) IsFinishTask(ctx context.Context, planID string) (bool, error) {
	planDao := dao.NewPlanDao()
	return planDao.IsFinishAllTasks(p.data.DB(ctx), planID)
}

func (p *PlanManageRepo) GetOngoingTasksItem(ctx context.Context, scope string) ([]*entity.TaskItem, error) {
	itemDao := dao.NewTaskItemDao()
	itemDao.GetOngoingTaskItems(p.data.DB(ctx), scope)
	return nil, nil
}

func (p *PlanManageRepo) FindOngoingPlanIds(ctx context.Context, userID string) ([]string, error) {
	planDao := dao.NewPlanDao()
	return planDao.FindOngoingPlanIds(p.data.DB(ctx), userID)
}

func (p *PlanManageRepo) GetOngoingTasksByPlanIds(ctx context.Context, planIds []string, scope string) ([]*entity.Task, error) {
	taskDao := dao.NewTaskDao()
	taskPOs, err := taskDao.FindOngoingTasksByPlanIds(p.data.DB(ctx), planIds, scope)
	if err != nil {
		return nil, err
	}
	return p.planManageConvertor.ConvertPO2TaskEntities(taskPOs), nil
}

func (p *PlanManageRepo) FindOngoingTaskItems(ctx context.Context, TodoTaskId string) ([]*entity.TaskItem, error) {
	itemDao := dao.NewTaskItemDao()
	itemPOs, err := itemDao.FindOngoing(p.data.DB(ctx), TodoTaskId)
	if err != nil {
		return nil, err
	}
	return p.planManageConvertor.ConvertPO2TaskItemEntities(itemPOs), nil
}

func (p *PlanManageRepo) EditTaskItem(ctx context.Context, plan *aggregate.PlanAggregate) error {
	taskPOs := p.planManageConvertor.EditTaskAndItemPO(plan)
	taskDao := dao.NewTaskDao()
	// 这里需要测试一下，如果是原本就有的，那会不会新增一个待办
	err := taskDao.Save(p.data.DB(ctx), taskPOs[0])
	if err != nil {
		return err
	}
	itemDao := dao.NewTaskItemDao()
	return itemDao.Edit(p.data.DB(ctx), taskPOs[0].TaskItemPOs[0])
}

func (p *PlanManageRepo) BatchDeleteQuestions(ctx context.Context, deleteQuestions []*entity.Question) error {
	questionsPOs := p.planManageConvertor.ConvertQuestionPO(deleteQuestions)
	questionDao := dao.NewQuestionDao()
	return questionDao.BatchDelete(p.data.DB(ctx), questionsPOs)
}

func (p *PlanManageRepo) SaveQuestions(ctx context.Context, plan *aggregate.PlanAggregate) error {
	questionsPOs := p.planManageConvertor.ConvertQuestionPO(plan.Questions)
	questionDao := dao.NewQuestionDao()
	return questionDao.Save(p.data.DB(ctx), questionsPOs)
}

func (p *PlanManageRepo) FinishPlan(ctx context.Context, plan *aggregate.PlanAggregate) error {
	planPO := p.planManageConvertor.FinishPlanPO(plan)
	planDao := dao.NewPlanDao()
	return planDao.Finish(p.data.DB(ctx), planPO)
}

func (p *PlanManageRepo) FindTaskByPlanIdAndDate(ctx context.Context, planId string, executeDate string) ([]*entity.Task, error) {
	taskDao := dao.NewTaskDao()
	taskPOs, err := taskDao.FindByPlanIdAndDate(p.data.DB(ctx), planId, executeDate)
	if err != nil {
		return nil, err
	}
	return p.planManageConvertor.ConvertPO2TaskEntities(taskPOs), nil
}

func (p *PlanManageRepo) FindAllPlanByUserId(ctx context.Context, userId string) ([]*aggregate.PlanAggregate, error) {
	planDao := dao.NewPlanDao()
	planPOs, err := planDao.FindAllByUserId(p.data.DB(ctx), userId)
	if err != nil {
		return nil, err
	}
	var res []*aggregate.PlanAggregate
	for _, planPO := range planPOs {
		planAggregate := p.planManageConvertor.CreatePlanAggregateByOption(convertor.WithPlanPO(planPO))
		res = append(res, planAggregate)
	}
	return res, nil
}

func (p *PlanManageRepo) FindAllTaskByPlanId(ctx context.Context, planID string) (*aggregate.PlanAggregate, error) {
	taskDao := dao.NewTaskDao()
	taskPOs, err := taskDao.FindAllByPlanId(p.data.DB(ctx), planID)
	if err != nil {
		return nil, err
	}
	return p.planManageConvertor.CreatePlanAggregateByOption(convertor.WithTaskPOs(taskPOs)), nil
}

func (p *PlanManageRepo) GetTaskItemByTaskId(ctx context.Context, taskId string) ([]*entity.TaskItem, error) {
	itemDao := dao.NewTaskItemDao()
	itemPOS, err := itemDao.FindByTaskId(p.data.DB(ctx), taskId)
	if err != nil {
		return nil, err
	}
	return p.planManageConvertor.ConvertPO2TaskItemEntities(itemPOS), nil
}

func (p *PlanManageRepo) GetOngoingPlanByUserId(ctx context.Context, userId string) (*aggregate.PlanAggregate, error) {
	planDao := dao.NewPlanDao()
	planPO, err := planDao.GetOngoingOneByUserId(p.data.DB(ctx), userId)
	if err != nil {
		return nil, err
	}
	return p.planManageConvertor.CreatePlanAggregateByOption(convertor.WithPlanPO(planPO)), nil
}
